package com.baturu.vin.benz.service.impl;

import com.baturu.vin.base.constant.MySQLVariables;
import com.baturu.vin.base.dto.BasePartsImageInfoDTO;
import com.baturu.vin.base.service.impl.EPCAnalyser;
import com.baturu.vin.benz.dal.dao.*;
import com.baturu.vin.benz.dto.*;
import com.baturu.vin.dto.*;
import com.baturu.vin.dto.carType.EPCCarTypeBenzDTO;
import com.baturu.vin.dto.parts.PartsAssemblyDTO;
import com.baturu.vin.dto.query.EPCPartsCodeResultDTO;
import com.baturu.vin.kit.StreamKit;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toMap;

/**
 * @author jiangxinlei
 * @Time 2017/6/1.
 */
@Slf4j
@Service("benzEPCAnalyser")
public class BenzEPCAnalyser extends EPCAnalyser<BenzVinInfoDTO, BenzPartsMappingDTO, BenzEPCPartsInfoDTO, EPCCarTypeBenzDTO> {

    @Autowired
    private BenzEPCPartsService benzEPCPartsService;
    @Autowired
    private BenzVinCarTypeInfoDAO benzVinCarTypeInfoDAO;
    @Autowired
    private BenzCarTypeInfoService benzCarTypeInfoService;
    @Autowired
    private BenzEPCPartsInfoDAO benzEPCPartsInfoDAO;
    @Autowired
    private BenzFilterService benzFilterService;
    @Autowired
    private BenzImageService benzImageService;
    @Autowired
    private BenzPartsMappingDAO benzPartsMappingDAO;
    @Autowired
    private BenzImagePartDAO benzImagePartDAO;
    @Autowired
    private BenzImageDAO benzImageDAO;
    @Autowired
    private BenzSaDAO saDAO;

    @Override
    public BenzVinInfoDTO analyseVinCode(String vinCode) {
        return benzEPCPartsService.getBenzVinCarTypeInfo(vinCode);
    }

    @Override
    public List<EPCCarTypeBenzDTO> queryCarTypes(BenzVinInfoDTO analyseResult) {
        if (analyseResult == null) {
            return Collections.emptyList();
        }

        String btrId = benzVinCarTypeInfoDAO.findBtrId(analyseResult);
        //找到车型
        if (StringUtils.isBlank(btrId)) {
            btrId = benzVinCarTypeInfoDAO.findBtrIdWithoutDisAndTrans(analyseResult);
        }
        if (StringUtils.isBlank(btrId)) {
            log.info("获取benz_vininfo_btr表的车型数据失败: {}", analyseResult.getVinCode());
            return Collections.emptyList();
        }
        //若有车型，获取装备和油漆编码
        fillPaintInterior(analyseResult);

        String saCodeStr = String.join(" ", analyseResult.getSaCodeSet());

        EPCCarTypeBenzDTO epcCarTypeBenzDTO = new EPCCarTypeBenzDTO();
        epcCarTypeBenzDTO.setBtrId(btrId);
        epcCarTypeBenzDTO.setVinCode(analyseResult.getVinCode());
        epcCarTypeBenzDTO.setInnerStyle(analyseResult.getInteriorDes());
        epcCarTypeBenzDTO.setOutStyle(analyseResult.getPaintDes());
        epcCarTypeBenzDTO.setSaCode(saCodeStr);
        epcCarTypeBenzDTO.setProductDateTime(analyseResult.getProTime());
        epcCarTypeBenzDTO.setTransmissionCode(analyseResult.getShortTransmission());
        epcCarTypeBenzDTO.setEngineCode(analyseResult.getShortEngine());
        return Lists.newArrayList(epcCarTypeBenzDTO);
    }

    @Override
    protected Map<String, List<BenzEPCPartsInfoDTO>> filterPartsInfoByVinAnalyseResult(Map<String, List<BenzEPCPartsInfoDTO>> partsInfoGroupByPartsName, BenzVinInfoDTO analyseResult) {
        if (MapUtils.isEmpty(partsInfoGroupByPartsName)) {
            return Collections.emptyMap();
        }
        return benzFilterService.filterGroupByPartsName(partsInfoGroupByPartsName, analyseResult);
    }

    private void fillPaintInterior(BenzVinInfoDTO carTypeInfoDTO) {
        List<String> sacodes = Lists.newArrayList();
        String paint = carTypeInfoDTO.getPaint1();
        String interior = carTypeInfoDTO.getInterior();
        if (StringUtils.isNotBlank(paint)) {
            sacodes.add(paint);
        }
        if (StringUtils.isNotBlank(interior)) {
            sacodes.add(interior);
        }
        if (CollectionUtils.isEmpty(sacodes)) {
            return;
        }
        String paintDes = null;
        String interiorDes = null;
        List<BenzVinInfoDTO> bvi = benzVinCarTypeInfoDAO.findSaDes(carTypeInfoDTO.getAdate(), carTypeInfoDTO.getChassbm().substring(0, 3), sacodes);
        if (CollectionUtils.isEmpty(bvi)) {
            return;
        }
        for (BenzVinInfoDTO benzVinInfoDTO : bvi) {
            String sa = benzVinInfoDTO.getSacodeex();
            String ades = benzVinInfoDTO.getAdesc();
            if (sa.equals(paint)) {
                paintDes = sa + " " + ades;
            }
            if (sa.equals(interior)) {
                interiorDes = sa + " " + ades;
            }
        }
        carTypeInfoDTO.setInteriorDes(interiorDes);
        carTypeInfoDTO.setPaintDes(paintDes);
    }

    @Override
    public List<BenzPartsMappingDTO> queryPartsMappings(Set<String> btrPartsNames, BenzVinInfoDTO analyseResult) {
        if (analyseResult == null) {
            return Collections.emptyList();
        }
        return benzCarTypeInfoService.getRelationDTOs(btrPartsNames, analyseResult.getCatNums());
    }

    @Override
    public List<BenzEPCPartsInfoDTO> queryPartsInfoByPartsMapping(List<BenzPartsMappingDTO> partsMappings, BenzVinInfoDTO analyseResult) {
        if (CollectionUtils.isEmpty(partsMappings)) {
            log.info("主名映射数据为空");
            return Collections.emptyList();
        }
        //这里 要注意同一个PartsCode 可能对应两个主名的情况
        Map<String, Set<String>> partsCodesMaps = partsMappings.stream().collect(toMap(BenzPartsMappingDTO::getPartsCode, b ->
                        Sets.newHashSet(b.getBtrPartsName()),
                (a, b) -> {
                    a.addAll(b);
                    return a;
                }));

        List<BenzEPCPartsInfoDTO> benzEPCPartsInfoDTOS = queryPartsInfoByPartsCode(partsCodesMaps.keySet(), analyseResult);
        Map<String, List<BenzEPCPartsInfoDTO>> benzEPCPartsInfoDTOSByPartsCode = benzEPCPartsInfoDTOS.stream().collect(groupingBy(BenzEPCPartsInfoDTO::getCbPartsCode));

        return partsCodesMaps.entrySet().stream()
                .filter(p -> CollectionUtils.isNotEmpty(benzEPCPartsInfoDTOSByPartsCode.get(p.getKey())))
                .map(parts -> {
                    List<BenzEPCPartsInfoDTO> benzEPCPartsInfoDTOList = benzEPCPartsInfoDTOSByPartsCode.get(parts.getKey());
                    List<BenzEPCPartsInfoDTO> resultBenzEPCPartsInfoDTOS = Lists.newArrayListWithExpectedSize(benzEPCPartsInfoDTOList.size() * parts.getValue().size());
                    parts.getValue().forEach(partsName ->
                            benzEPCPartsInfoDTOList.forEach(b -> {
                                //deep copy
                                BenzEPCPartsInfoDTO benzEPCPartsInfoDTO = new BenzEPCPartsInfoDTO();
                                BeanUtils.copyProperties(b, benzEPCPartsInfoDTO);
                                benzEPCPartsInfoDTO.setBtrPartsName(partsName);
                                resultBenzEPCPartsInfoDTOS.add(benzEPCPartsInfoDTO);
                            }));
                    return resultBenzEPCPartsInfoDTOS;
                }).flatMap(Collection::stream).collect(Collectors.toList());
    }

    @Override
    public List<BenzEPCPartsInfoDTO> queryPartsInfoByPartsCode(Set<String> partsCodes) {
        return benzCarTypeInfoService.queryPartsInfoByPartsCode(partsCodes);
    }

    @Override
    public List<BenzEPCPartsInfoDTO> queryPartsInfoByPartsCode(Set<String> partsCodes, BenzVinInfoDTO analyseResult) {
        return benzCarTypeInfoService.findPartsByPartsCodeAndCatNum(partsCodes, analyseResult.getCatNums());
    }

    @Override
    public List<BenzEPCPartsInfoDTO> filterPartsInfoByVinAnalyseResult(List<BenzEPCPartsInfoDTO> partsInfo, BenzVinInfoDTO analyseResult) {
        if (CollectionUtils.isEmpty(partsInfo)) {
            return Collections.emptyList();
        }
      return  benzFilterService.filterByRules(partsInfo, analyseResult);
    }

    @Override
    public Map<String, List<EPCPartsImageDTO>> queryPartsImage(Map<String, List<BenzEPCPartsInfoDTO>> partsInfoGroup, BenzVinInfoDTO analyseResult) {
        if (MapUtils.isEmpty(partsInfoGroup)) {
            return Maps.newHashMap();
        }
        List<BenzEPCPartsInfoDTO> benzEPCPartsInfoDTOS = partsInfoGroup.values().stream().flatMap(Collection::stream).collect(Collectors.toList());
        benzImageService.fillImageIntoParts(benzEPCPartsInfoDTOS);

        Map<String, List<BenzEPCPartsInfoDTO>> collect = benzEPCPartsInfoDTOS.stream().collect(groupingBy(BenzEPCPartsInfoDTO::getPartsCode));
        return collect.entrySet().stream().collect(toMap(Map.Entry::getKey,
                d -> d.getValue().stream().filter(z -> StringUtils.isNotBlank(z.getImgPath())).map(z -> {
                    EPCPartsImageDTO imgDTO = new EPCPartsImageDTO();
                    imgDTO.setEpcPartsImagePositionDTOs(z.getPositon());
                    imgDTO.setImageName(z.getImgName());
                    imgDTO.setImageCode(z.getImgName());
                    imgDTO.setImageUrl(z.getImgPath());
                    return imgDTO;
                }).distinct().collect(Collectors.toList())));
    }

    @Override
    public Map<String, String> queryColorRemark(Map<String, List<BenzEPCPartsInfoDTO>> partsInfoGroup, BenzVinInfoDTO analyseResult) {
        return partsInfoGroup.keySet().stream().collect(Collectors.toMap(e -> e, p -> {
            List<BenzEPCPartsInfoDTO> benzEPCPartsInfoDTOS = partsInfoGroup.get(p);
            if (CollectionUtils.isEmpty(benzEPCPartsInfoDTOS)) {
                return StringUtils.EMPTY;
            }
            return getColorRemark(benzEPCPartsInfoDTOS);
        }));
    }

    @Override
    public List<BenzPartsMappingDTO> queryPartsMappingByPartsInfoAndAnalyseResult(Map<String, List<BenzEPCPartsInfoDTO>> partsInfoGroup, BenzVinInfoDTO analyseResult) {
        Map<String, String> codeMap = partsInfoGroup.values().stream().flatMap(Collection::stream)
                .collect(toMap(BenzEPCPartsInfoDTO::getCbPartsCode, BenzEPCPartsInfoDTO::getPartsCode, (e1, e2) -> e2));
        List<BenzPartsMappingDTO> benzPartsMappingDTOS = StreamKit.partitionStream(codeMap.keySet(), SEGMENT_QUERY_SIZE).flatMap(codes ->
                benzPartsMappingDAO.getByPartsCode(codes).stream()
        ).collect(Collectors.toList());
        for (BenzPartsMappingDTO benzPartsMappingDTO : benzPartsMappingDTOS) {
            String priCode = codeMap.get(benzPartsMappingDTO.getPartsCode());
            benzPartsMappingDTO.setPartsCode(priCode);
        }
        return benzPartsMappingDTOS;
    }

    private String getColorRemark(List<BenzEPCPartsInfoDTO> filterInfos) {
        Set<String> colorRemarks = filterInfos.stream().map(BenzEPCPartsInfoDTO::getColorRemark).collect(Collectors.toSet());
        return combineString(colorRemarks);
    }

    @Override
    public List<BtrVinInfoDTO> transformBtrVinInfo(String vinCode, List<EPCCarTypeBenzDTO> epcCarTypes) {
        return epcCarTypes.stream().map(o ->
                BtrVinInfoDTO.builder()
                        .btrId(o.getBtrId())
                        .vinCode(o.getVinCode())
                        .innerStyle(o.getInnerStyle())
                        .outStyle(o.getOutStyle())
                        .saCode(o.getSaCode())
                        .productDateTime(o.getProductDateTime())
                        .build()
        ).collect(Collectors.toList());
    }

    @Override
    public Map<String, List<BenzEPCPartsInfoDTO>> getPartsInfoGroupByPartsName(List<BenzEPCPartsInfoDTO> partsInfo, List<BenzPartsMappingDTO> partsMappingList) {
        return partsInfo.stream().collect(Collectors.groupingBy(BenzEPCPartsInfoDTO::getBtrPartsName));
    }

    @Override
    public List<EPCPartsDTO> getEPCPartsDTOListFromBtrPartsByPartsName(Set<String> btrPartsNames, BenzVinInfoDTO analyseResult) {


        if (CollectionUtils.isEmpty(btrPartsNames) || Objects.isNull(analyseResult)) {
            return Collections.emptyList();
        }
        //非油类自编码
        Set<BenzSelfPartsNameMappingDTO> benzSelfPartsNameMappingDTOS = queryBtrParts(btrPartsNames, analyseResult);
        //油类自编码
        Set<BenzSelfPartsNameMappingDTO> benzSelfOilPartsNameMappingDTOS1 = queryBtrOilParts(btrPartsNames, analyseResult);

        Set<BenzSelfPartsNameMappingDTO> selfCodes = Sets.union(benzSelfPartsNameMappingDTOS,benzSelfOilPartsNameMappingDTOS1);
        if (CollectionUtils.isEmpty(selfCodes)) {
            return Collections.emptyList();
        }

        return selfCodes.stream().map(self -> {
            EPCPartsDTO epcPartsDTO = new EPCPartsDTO();
            epcPartsDTO.setBtrPartsName(self.getPartsName());
            epcPartsDTO.setPartsCode(self.getPartsCode());
            return epcPartsDTO;
        }).collect(Collectors.toList());

    }

    private Set<BenzSelfPartsNameMappingDTO> queryBtrOilParts(Set<String> btrPartsNames, BenzVinInfoDTO analyseResult) {
        List<EPCPartsDTO> epcPartsDTOS = benzEPCPartsService.fillExtraNameCodeRelation(btrPartsNames, analyseResult);
        return epcPartsDTOS.stream().map(l -> BenzSelfPartsNameMappingDTO.builder().partsName(l.getBtrPartsName()).partsCode(l.getPartsCode()).build()).collect(Collectors.toSet());
    }

    private Set<BenzSelfPartsNameMappingDTO> queryBtrParts(Set<String> btrPartsNames, BenzVinInfoDTO analyseResult) {
        if (CollectionUtils.isEmpty(btrPartsNames)) {
            return Sets.newHashSet();
        }

        Set<BenzSelfPartsNameMappingDTO> benzSelfPartsNameMappingDTOS=benzPartsMappingDAO.findSelfPartsNotOilMapping(btrPartsNames);

        if (CollectionUtils.isEmpty(benzSelfPartsNameMappingDTOS)) {
            return Collections.emptySet();
        }
        Set<String> getStdPartsName = benzSelfPartsNameMappingDTOS.stream().map(BenzSelfPartsNameMappingDTO::getStdPartsName).filter(StringUtils::isNoneBlank).collect(Collectors.toSet());

        List<EPCPartsDTO> epcPartsList = getStandPartsInfo(getStdPartsName, analyseResult);

        if (CollectionUtils.isEmpty(epcPartsList)) {
            return Collections.emptySet();
        }

        Map<String, List<EPCPartsDTO>> partsMap = epcPartsList.stream().collect(Collectors.groupingBy(EPCPartsDTO::getBtrPartsName));
        return benzSelfPartsNameMappingDTOS.stream()
                .filter(p -> CollectionUtils.isNotEmpty(partsMap.get(p.getStdPartsName())))
                .flatMap(p->{
                    List<EPCPartsDTO> epcPartsDTOS = partsMap.get(p.getStdPartsName());
                    return epcPartsDTOS.stream().map(l->
                            BenzSelfPartsNameMappingDTO.builder()
                                    .partsName(p.getBasicPartsName())
                                    .partsCode(l.getPartsCode() + p.getDistinguishMarks() + p.getSuffix())
                                    .build()
                    );
                }).collect(Collectors.toSet());
    }

    private   List<EPCPartsDTO> getStandPartsInfo(Set<String> btrPartsNames,BenzVinInfoDTO analyseResult){
        List<BenzPartsMappingDTO> partsMappings = queryPartsMappingsForCall(btrPartsNames, analyseResult);
        List<BenzEPCPartsInfoDTO> partsInfo = queryPartsInfoByPartsMappingForCall(partsMappings, analyseResult);
        partsInfo = filterPartsInfoByVinAnalyseResultForCall(partsInfo, analyseResult);
        Map<String, List<BenzEPCPartsInfoDTO>>  partsInfoGroupByPartsName = getPartsInfoGroupByPartsNameForCall(partsInfo, partsMappings);
        partsInfoGroupByPartsName = filterPartsInfoByVinAnalyseResultForCall(partsInfoGroupByPartsName, analyseResult);
        return getEPCPartsDTOListForCall(partsInfoGroupByPartsName, analyseResult, null);
    }

    @Override
    public List<EPCPartsDTO> getEPCPartsDTOListFromBtrPartsByPartsCode(Set<String> partsCodes, BenzVinInfoDTO analyseResult) {
        return benzEPCPartsService.fillExtraCodeRelation(partsCodes, analyseResult);
    }

    @Override
    public List<EPCPartsCodeResultDTO> queryBtrPartsCode(Set<String> partsCodes) {
        if (CollectionUtils.isEmpty(partsCodes)) {
            return Collections.emptyList();
        }
        List<BenzPartsMappingDTO> extraRelationDTOs = benzPartsMappingDAO.findRelationByExtraPartsCode(partsCodes);
        if (CollectionUtils.isEmpty(extraRelationDTOs)) {
            return Collections.emptyList();
        }
        return extraRelationDTOs.stream().filter(l ->
                StringUtils.isNotBlank(l.getPartsCode())
        ).map(l ->
                EPCPartsCodeResultDTO.builder().partsCode(l.getPartsCode()).build()
        ).distinct().collect(Collectors.toList());
    }

    @Override
    public Map<String, Set<String>> queryReplaceCodes(Set<String> partsCodes, BenzVinInfoDTO analyseResult) {
        if (analyseResult == null || CollectionUtils.isEmpty(analyseResult.getCatNums())) {
            return Collections.emptyMap();
        }
        List<BenzReplaceCodeDTO> benzReplaceCodeDTOS = benzEPCPartsInfoDAO.queryReplaceCodes(partsCodes, analyseResult.getCatNums());
        if (CollectionUtils.isEmpty(benzReplaceCodeDTOS)) {
            return Collections.emptyMap();
        }
        Map<String, List<BenzReplaceCodeDTO>> repMap = benzReplaceCodeDTOS.stream().collect(groupingBy(BenzReplaceCodeDTO::getPartsCode));
        return partsCodes.stream().collect(Collectors.toMap(o -> o, partsCode -> Optional.ofNullable(repMap.get(partsCode))
                .map(partsCodeList -> partsCodeList.stream().map(BenzReplaceCodeDTO::getReplaceCode).collect(Collectors.toSet()))
                .orElseGet(Collections::emptySet)));
    }

    @Override
    protected Map<String, String> queryInstallNum(Map<String, List<BenzEPCPartsInfoDTO>> partsInfoGroup, BenzVinInfoDTO analyseResult) {
        return partsInfoGroup.keySet().stream().collect(Collectors.toMap(e -> e, p -> {
            List<BenzEPCPartsInfoDTO> benzEPCPartsInfoDTOS = partsInfoGroup.get(p);
            if (CollectionUtils.isEmpty(benzEPCPartsInfoDTOS)) {
                return StringUtils.EMPTY;
            }
            Set<String> installNumber = benzEPCPartsInfoDTOS.stream().filter(z -> StringUtils.isNotBlank(z.getQuantbm()) && z.getQuantbm().length() >= 3)
                    .map(z -> StringUtils.substring(z.getQuantbm(), 0, 3)).collect(Collectors.toSet());
            return combineString(installNumber);
        }));
    }

    @Override
    protected Map<String, List<BasePartsImageInfoDTO>> queryEPCImageInfo(Set<String> sortNames, BenzVinInfoDTO analyseResult) {
        if (CollectionUtils.isEmpty(sortNames) || analyseResult == null || CollectionUtils.isEmpty(analyseResult.getCatNums())) {
            return Collections.emptyMap();
        }
        //获取映射数据
        List<BenzImagePartDTO> benzImagePartDTOS = benzImagePartDAO.queryImageBySortName(sortNames);
        if (CollectionUtils.isEmpty(benzImagePartDTOS)) {
            return Collections.emptyMap();
        }
        Set<String> imageCodes = benzImagePartDTOS.stream().map(BenzImagePartDTO::getImageCode).collect(Collectors.toSet());
        //通过catnum获取该目录下的所有图片信息
        List<BenzImageDTO> benzImageDTOS = benzImageDAO.queryImageCodeByCatNums(analyseResult.getCatNums());
        if (CollectionUtils.isEmpty(benzImageDTOS)) {
            return Collections.emptyMap();
        }
        //过滤
        benzImageDTOS.removeIf(p -> !imageCodes.contains(p.getImageCode()));
        if (CollectionUtils.isEmpty(benzImageDTOS)) {
            return Collections.emptyMap();
        }

        //完善imgUrl
        for (BenzImageDTO benzImageDTO : benzImageDTOS) {
            String imageUrl = benzImageDTO.getImgtype() + benzImageDTO.getGroupnum() + benzImageDTO.getSubgrp() + "/" + benzImageDTO.getImageCode() + ".png";
            String imgPath = appConfigLoader.getEPCPartsImageFullUrl(EPCAnalyserEnum.BENZ, imageUrl);
            benzImageDTO.setImageUrl(imgPath);
        }

        Map<String, List<BenzImageDTO>> imageCodeMap = benzImageDTOS.stream().collect(Collectors.groupingBy(BenzImageDTO::getImageCode));
        Map<String, List<BenzImagePartDTO>> mapBySortName = benzImagePartDTOS.stream().collect(Collectors.groupingBy(BenzImagePartDTO::getSortName));

        return sortNames.stream().collect(Collectors.toMap(e -> e, sortName -> {
            List<BenzImagePartDTO> imagePartDTOs = mapBySortName.get(sortName);
            if (CollectionUtils.isEmpty(imagePartDTOs)) {
                return Collections.emptyList();
            }
            return imagePartDTOs.stream().filter(p -> CollectionUtils.isNotEmpty(imageCodeMap.get(p.getImageCode())))
                    .flatMap(p -> imageCodeMap.get(p.getImageCode()).stream()).distinct().collect(Collectors.toList());
        }));
    }

    @Override
    protected List<EPCPartsImageDTO> queryEPCImageInfoByImageCode(Set<String> imageCodes, BenzVinInfoDTO analyseResult) {
        if (CollectionUtils.isEmpty(imageCodes) || analyseResult == null || CollectionUtils.isEmpty(analyseResult.getCatNums())) {
            return Collections.emptyList();
        }

        //通过catnum获取该目录下的所有图片信息
        List<BenzImageDTO> benzImageDTOS = benzImageDAO.queryImageCode(analyseResult.getCatNums(), Collections.EMPTY_LIST);
        if (CollectionUtils.isEmpty(benzImageDTOS)) {
            return Collections.emptyList();
        }
        //过滤
        benzImageDTOS.removeIf(p -> !imageCodes.contains(p.getImageCode()) || CollectionUtils.isEmpty(p.getCallouts()));
        if (CollectionUtils.isEmpty(benzImageDTOS)) {
            return Collections.emptyList();
        }

        List<BenzImgPositionDTO> positionDTOS = benzImageDTOS.stream()
                .flatMap(p -> p.getCallouts().stream().map(callout -> BenzImgPositionDTO.builder().imgName(p.getImageCode()).callout(callout).build()))
                .collect(Collectors.toList());

        //根据imageCode和callout拿出所有位置信息
        List<BenzImgPositionDTO> positionWithHotPoints = benzImageDAO.findImgPositionByQueryParam(positionDTOS);
        if (CollectionUtils.isEmpty(positionWithHotPoints)) {
            return Collections.emptyList();
        }

        Map<String, String> catalogMap = benzImageDTOS.stream().collect(Collectors.toMap(BenzImageDTO::getImageCode, BenzImageDTO::getCatalog));

        Map<String, List<BenzImgPositionDTO>> positionByImageCode = positionWithHotPoints.stream().collect(Collectors.groupingBy(BenzImgPositionDTO::getImgName));


        return positionByImageCode.keySet().stream()
                .filter(p -> CollectionUtils.isNotEmpty(positionByImageCode.get(p)))
                .map(imageCode -> {
                    EPCPartsImageDTO epcPartsImageDTO = new EPCPartsImageDTO();
                    epcPartsImageDTO.setImageCode(imageCode);
                    String imageUrl = catalogMap.get(imageCode) + "/" + imageCode + ".png";
                    String imgPath = appConfigLoader.getEPCPartsImageFullUrl(EPCAnalyserEnum.BENZ, imageUrl);
                    epcPartsImageDTO.setImageUrl(imgPath);
                    List<EPCPartsImagePositionDTO> epcPartsImagePositionDTOs = transferToPositionDTO(positionByImageCode.get(imageCode));
                    epcPartsImageDTO.setEpcPartsImagePositionDTOs(epcPartsImagePositionDTOs);
                    return epcPartsImageDTO;
                }).collect(Collectors.toList());
    }

    @Override
    protected List<BenzEPCPartsInfoDTO> queryPartsInfoByImageCode(Set<String> imageCodes, BenzVinInfoDTO analyseResult) {
        if (CollectionUtils.isEmpty(imageCodes) || analyseResult == null || CollectionUtils.isEmpty(analyseResult.getCatNums())) {
            return Collections.emptyList();
        }

        //通过catnum获取该目录下的所有图片信息
        List<BenzImageDTO> benzImageDTOS = benzImageDAO.queryImageCode(analyseResult.getCatNums(), Collections.EMPTY_LIST);
        if (CollectionUtils.isEmpty(benzImageDTOS)) {
            return Collections.emptyList();
        }
        //过滤
        benzImageDTOS.removeIf(p -> !imageCodes.contains(p.getImageCode()));
        if (CollectionUtils.isEmpty(benzImageDTOS)) {
            return Collections.emptyList();
        }
        //根据catnum,groupnum,subgrp带出callout和partsCode
        List<BenzEPCPartsInfoDTO> partDTOs = benzImagePartDAO.queryPartsCodeByImageCode(benzImageDTOS);
        if (CollectionUtils.isEmpty(partDTOs)) {
            return Collections.emptyList();
        }

        List<BenzEPCPartsInfoDTO> partsInfo = partDTOs.stream().filter(p -> benzImageDTOS.stream().anyMatch(imageDTO -> p.getCatNum().equalsIgnoreCase(imageDTO.getCatnum()) && p.getGroupNum().equalsIgnoreCase(imageDTO.getGroupnum())
                && p.getSubGroup().equalsIgnoreCase(imageDTO.getSubgrp()) && benzImageService.containCallout(p.getCallout(), imageDTO.getCallout()))).distinct().collect(Collectors.toList());

        return filterPartsInfoByVinAnalyseResult(partsInfo, analyseResult);
    }

    @Override
    public Map<String, String> queryEpcPartsName(Map<String, List<BenzEPCPartsInfoDTO>> partsInfoGroup, BenzVinInfoDTO analyseResult) {
        return partsInfoGroup.keySet().stream().collect(Collectors.toMap(e -> e, p -> {
            List<BenzEPCPartsInfoDTO> benzEPCPartsInfoDTOS = partsInfoGroup.get(p);
            if (CollectionUtils.isEmpty(benzEPCPartsInfoDTOS)) {
                return StringUtils.EMPTY;
            }
            List<String> epcPartsNames = benzEPCPartsInfoDTOS.stream().map(BenzEPCPartsInfoDTO::getEpcPartsName).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(epcPartsNames)) {
                return StringUtils.EMPTY;
            }
            return epcPartsNames.get(0);
        }));
    }

    private String combineString(Set<String> set) {
        String str = "";
        if (CollectionUtils.isNotEmpty(set)) {
            for (String remark : set) {
                if (StringUtils.isNotBlank(remark)) {
                    str = remark + "/";
                }
            }
            if (StringUtils.isNotBlank(str)) {
                str = str.substring(0, str.length() - 1);
            }
        }
        return str;
    }

    @Override
    protected List<PartsAssemblyDTO> queryPartsAssembly(Set<String> imageCodes, Set<String> partsNames, BenzVinInfoDTO analyseResult) {
        if (CollectionUtils.isEmpty(imageCodes) || CollectionUtils.isEmpty(partsNames) || analyseResult == null) {
            return Collections.emptyList();
        }
        //根据主名获取编码信息(主要得到catnum,groupnum,subgrp,cbPartsCode,并且因为是总成主名,那么indent=0)
        List<BenzPartsMappingDTO> partsMappings = StreamKit.partitionStream(partsNames, SEGMENT_QUERY_SIZE).flatMap(list -> queryPartsMappings(new HashSet<>(list), analyseResult).stream()).distinct().collect(Collectors.toList());
        List<BenzEPCPartsInfoDTO> partsInfo = queryAssemblyPartsByPartsName(partsMappings, imageCodes, analyseResult);

        if (CollectionUtils.isEmpty(partsInfo)) {
            return Collections.emptyList();
        }

        Map<String, List<BenzEPCPartsInfoDTO>> partsInfoGroupByPartsName = getPartsInfoGroupByPartsName(partsInfo, partsMappings);

        //根据catnum, groupnum, subgrp获取这下面的所有编码
        List<BenzEPCPartsInfoDTO> allPartsInfo = queryPartsByCatNumGroupNumSubGrp(partsInfo, analyseResult);

        if (CollectionUtils.isEmpty(allPartsInfo)) {
            return Collections.emptyList();
        }

        Map<String, Map<String, Set<String>>> nonAssPartByPartsName = Maps.newHashMap();

        Set<String> allNonPartsCode = Sets.newHashSet();

        partsInfoGroupByPartsName.keySet().forEach(partsName -> {
            List<BenzEPCPartsInfoDTO> partsInfoDTOs = partsInfoGroupByPartsName.get(partsName);
            Map<String, List<BenzEPCPartsInfoDTO>> partsByImageCode = partsInfoDTOs.stream().collect(Collectors.groupingBy(BenzEPCPartsInfoDTO::getImageCode));

            Map<String, Set<String>> nonAssPartsCodeByImageCode = Maps.newHashMap();

            partsByImageCode.keySet().forEach(imageCode -> {
                Set<String> nonAssPartsCode = Sets.newHashSet();
                partsInfoDTOs.forEach(q -> {
                    //catnum, groupnum, subgrp, callout可以确定一张图,做为过滤条件
                    //非总成位置号一定在总成编码后面
                    List<BenzEPCPartsInfoDTO> filteredParts = allPartsInfo.stream().filter(z -> z.getCatNum().equals(q.getCatNum()) && z.getGroupNum().equals(q.getGroupNum()) && z.getSubGroup().equals(q.getSubGroup()) && q.getImageCallout().contains(benzImageService.getStandardCallout(z.getCallout())))
                            .filter(z -> z.getCallout().compareTo(q.getCallout()) > 0).collect(Collectors.toList());
                    if (CollectionUtils.isEmpty(filteredParts)) {
                        return;
                    }

                    filteredParts.sort(Comparator.comparing(BenzEPCPartsInfoDTO::getSeqnum));
                    Iterator<BenzEPCPartsInfoDTO> iterator = filteredParts.iterator();
                    //按照原有编码的callout的下一个最近的位置来看indent是否为1,若为1,则看下一个,直到为0,那么这些编码就是该编码的非总成编码
                    while (iterator.hasNext()) {
                        BenzEPCPartsInfoDTO nextPart = iterator.next();
                        String indent = StringUtils.isBlank(nextPart.getIndent()) ? "0" : nextPart.getIndent();
                        if ("0".equals(indent)) {
                            break;
                        }
                        nonAssPartsCode.add(nextPart.getPartsCode());
                        allNonPartsCode.add(nextPart.getPartsCode());
                    }
                });
                if (CollectionUtils.isNotEmpty(nonAssPartsCode)) {
                    nonAssPartsCodeByImageCode.put(imageCode, nonAssPartsCode);
                }
            });
            if (MapUtils.isNotEmpty(nonAssPartsCodeByImageCode)) {
                nonAssPartByPartsName.put(partsName, nonAssPartsCodeByImageCode);
            }
        });

        if (CollectionUtils.isEmpty(allNonPartsCode)) {
            return Collections.emptyList();
        }

        //非总成编码获取巴图鲁主名
        List<BenzPartsMappingDTO> benzPartsMappingDTOS = StreamKit.partitionStream(allNonPartsCode, SEGMENT_QUERY_SIZE).flatMap(codes ->
                benzPartsMappingDAO.getByPartsCode(codes).stream()
        ).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(benzPartsMappingDTOS)) {
            return Collections.emptyList();
        }

        Map<String, List<BenzPartsMappingDTO>> partsMappingByCode = benzPartsMappingDTOS.stream().collect(Collectors.groupingBy(BenzPartsMappingDTO::getPartsCode));

        List<PartsAssemblyDTO> dtos = Lists.newArrayList();

        nonAssPartByPartsName.keySet().forEach(partsName -> {
            Map<String, Set<String>> nonAssPartsByImageCode = nonAssPartByPartsName.get(partsName);
            nonAssPartsByImageCode.keySet().forEach(imageCode -> {
                Set<String> partsCode = nonAssPartsByImageCode.get(imageCode);
                partsCode.stream().filter(p -> CollectionUtils.isNotEmpty(partsMappingByCode.get(p))).forEach(p -> {
                    List<BenzPartsMappingDTO> mappingDTOs = partsMappingByCode.get(p);
                    mappingDTOs.forEach(mappingDTO -> {
                        PartsAssemblyDTO partsAssemblyDTO = PartsAssemblyDTO.builder().assPartsName(partsName).imageCode(imageCode).nonAssPartsName(mappingDTO.getBtrPartsName()).build();
                        dtos.add(partsAssemblyDTO);
                    });
                });
            });

        });
        return dtos;
    }

    private List<EPCPartsImagePositionDTO> transferToPositionDTO(List<BenzImgPositionDTO> positions) {
        return positions.stream().map(postionDTO -> {
            EPCPartsImagePositionDTO epip = new EPCPartsImagePositionDTO();
            EPCImagePointDTO topLeft = new EPCImagePointDTO();
            EPCImagePointDTO bottomRight = new EPCImagePointDTO();
            topLeft.setX(postionDTO.getTopLeftX());
            topLeft.setY(postionDTO.getTopLeftY());
            bottomRight.setX(postionDTO.getBottomRightX());
            bottomRight.setY(postionDTO.getBottomRightY());
            epip.setPosition(postionDTO.getCallout());
            epip.setTopLeft(topLeft);
            epip.setBottomRight(bottomRight);
            return epip;
        }).collect(Collectors.toList());
    }

    private List<BenzEPCPartsInfoDTO> queryPartsByCatNumGroupNumSubGrp(List<BenzEPCPartsInfoDTO> partsInfo, BenzVinInfoDTO analyseResult) {
        List<BenzEPCPartsInfoDTO> allPartsInfo = StreamKit.partitionStream(partsInfo, SEGMENT_QUERY_SIZE)
                .flatMap(list -> benzEPCPartsInfoDAO.findPartsByCatNumGroupNumSubGrp(list).stream()).distinct().collect(Collectors.toList());
        //所有编码过解析器
        allPartsInfo = filterPartsInfoByVinAnalyseResult(allPartsInfo, analyseResult);
        return allPartsInfo;
    }

    private List<BenzEPCPartsInfoDTO> queryAssemblyPartsByPartsName(List<BenzPartsMappingDTO> partsMappings, Set<String> imageCodes, BenzVinInfoDTO analyseResult) {
        List<BenzEPCPartsInfoDTO> partsInfo = queryPartsInfoByPartsMapping(partsMappings, analyseResult);
        partsInfo = filterPartsInfoByVinAnalyseResult(partsInfo, analyseResult);

        //筛选出indent为0的配件,indent不为0表示是非总成
        partsInfo.removeIf(p -> !"0".equals(p.getIndent()));
        if (CollectionUtils.isEmpty(partsInfo)) {
            return Collections.emptyList();
        }

        benzImageService.fillImageIntoParts(partsInfo);

        partsInfo.removeIf(p -> !imageCodes.contains(p.getImageCode()));
        return partsInfo;
    }


    @Override
    protected Map<String, Set<String>> queryFeatures(Map<String, List<BenzEPCPartsInfoDTO>> partsInfoGroup, BenzVinInfoDTO analyseResult) {
        if (MapUtils.isEmpty(partsInfoGroup)) {
            return Collections.emptyMap();
        }
        List<Map<String, String>> list = saDAO.queryAllFeaturesMapping();
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyMap();
        }

        Map<String, String> featureMapping = list.stream().collect(Collectors.toMap(l -> l.get("saCode"), o -> o.get("translation")));
        if (MapUtils.isEmpty(featureMapping)) {
            return Collections.emptyMap();
        }

        return partsInfoGroup.keySet().stream().collect(Collectors.toMap(
                k -> k,
                key -> {
                    List<BenzEPCPartsInfoDTO> partsInfo = partsInfoGroup.get(key);
                    if (CollectionUtils.isEmpty(partsInfo)) {
                        return Collections.emptySet();
                    }

                    Set<String> featureDescriptions = Sets.newHashSet();

                    partsInfo.forEach(p -> {
                        String saCode = p.getSaCode();
                        if (StringUtils.isBlank(saCode)) {
                            return;
                        }

                        if (!"+".equals(saCode.substring(0, 1)) && !"-".equals(saCode.substring(0, 1))) {
                            saCode = "+" + saCode;
                        }
                        saCode = StringUtils.replace(saCode, "+", " +");
                        saCode = StringUtils.replace(saCode, "-", " -");

                        String[] split = saCode.split(" ");
                        for (String sa : split) {
                            if (StringUtils.isBlank(sa)) {
                                continue;
                            }
                            String descPre = "适用于";
                            if (sa.startsWith("-")) {
                                descPre = "不" + descPre;
                            }
                            String dealSa = sa.replaceAll("\\+|-|\\(|\\)|\\[|]", "");
                            String[] detail = dealSa.split("/");
                            for (String s : detail) {
                                String desc = featureMapping.get(s);
                                if (StringUtils.isNotBlank(desc)) {
                                    featureDescriptions.add(descPre + desc);
                                }
                            }
                        }
                    });
                    return featureDescriptions;
                }
        ));
    }

    /**
     * 使用车架号过滤非该车架号所属配件的EPC配件主名
     *
     * @param vinCode       车架号
     * @param epcPartsNames BTR配件主名
     * @return 属于该车架号下的EPC配件主名
     */
    @Override
    protected Set<String> filterEPCPartsNamesByVinCode(String vinCode, Set<String> epcPartsNames) {
        if (CollectionUtils.isEmpty(epcPartsNames)) {
            return Collections.emptySet();
        }
        BenzVinInfoDTO benzVinInfoDTO = analyseVinCode(vinCode);
        if (Objects.isNull(benzVinInfoDTO)) {
            return Collections.emptySet();
        }
        Map<String, BenzPartCatInfoDTO> catInfoMap = benzVinInfoDTO.getCatInfoMap();
        if (MapUtils.isEmpty(catInfoMap)) {
            return Collections.emptySet();
        }
        Set<String> catNum = catInfoMap.values().stream()
                .map(BenzPartCatInfoDTO::getCatalog)
                .collect(Collectors.toSet());
        List<BenzEPCPartsNameDTO> benzNoun = StreamKit.partitionStream(epcPartsNames, MySQLVariables.EQ_RANGE_INDEX_DIVE_LIMIT).flatMap(l -> benzEPCPartsInfoDAO.queryPartEpcNoun(l).stream()).collect(Collectors.toList());
        Set<String> benzNounIdx = benzNoun.stream().map(BenzEPCPartsNameDTO::getNounIdx).collect(Collectors.toSet());

        List<String> benzNounIdxHas = StreamKit.partitionStream(benzNounIdx, MySQLVariables.EQ_RANGE_INDEX_DIVE_LIMIT).flatMap(l -> benzEPCPartsInfoDAO.findPartsByNounIdx(benzNounIdx, catNum).stream()).collect(Collectors.toList());
        return benzNoun.stream().filter(l -> benzNounIdxHas.contains(l.getNounIdx())).map(BenzEPCPartsNameDTO::getNoun).collect(Collectors.toSet());
    }

}
